﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xrm.Sdk.Client;
using Microsoft.Xrm.Sdk.Messages;
using Microsoft.Crm.Sdk.Samples;
using Microsoft.Xrm.Sdk.Discovery;
using Microsoft.Crm.Sdk.Messages;
using System.ServiceModel.Security;
using Microsoft.Xrm.Sdk.Query;
using Microsoft.Xrm.Sdk;
using ICSharpCode.SharpZipLib.Zip;
using System.IO;
namespace Crm2011EventBinder.Async
{
    
    class CrmAsyncHelper
    {

        public object CrmAsyncLock = new object();

        public delegate void dRetrieveEntityList(OrganizationServiceProxy Service, List<EntityContainer> EntityContainers);
        public void RetrieveEntityList(OrganizationServiceProxy Service, List<EntityContainer> EntityContainers)
        {

            RetrieveAllEntitiesRequest Req;
            RetrieveAllEntitiesResponse Resp;

            //prevent other threads from using Service object.
            lock (CrmAsyncLock)
            {
                Req = new RetrieveAllEntitiesRequest();
                Req.RetrieveAsIfPublished = false;
                Resp = (RetrieveAllEntitiesResponse)Service.Execute(Req);
            }

            EntityContainers.Clear();
            foreach (Microsoft.Xrm.Sdk.Metadata.EntityMetadata x in Resp.EntityMetadata.Where(x => x.DisplayName.UserLocalizedLabel != null && x.IsCustomizable.Value).OrderBy(x => x.DisplayName.UserLocalizedLabel.Label))
                EntityContainers.Add(new EntityContainer(x));
            
        }

        public delegate void dRetrieveOrganizations(ServerConnection.Configuration Config, List<OrganizationDetailContainer> OrganizationDetailContainers);
        public void RetrieveOrganizations(ServerConnection.Configuration Config, List<OrganizationDetailContainer> OrganizationDetailContainers)
        {

            lock (CrmAsyncLock)
            {
                OrganizationDetailCollection Organizations = ServerConnection.RetrieveOrganizations(Config);

                OrganizationDetailContainers.Clear();
                foreach (OrganizationDetail x in Organizations)
                    OrganizationDetailContainers.Add(new OrganizationDetailContainer(x));
            }
        }

        public delegate void dWhoAmI(OrganizationServiceProxy Service, ref Boolean InvalidLogin);
        public void WhoAmI(OrganizationServiceProxy Service, ref Boolean InvalidLogin)
        {

            WhoAmIRequest Req;
            WhoAmIResponse Resp;

            try
            {
                lock (CrmAsyncLock)
                {
                    Req = new WhoAmIRequest();
                    Resp = (WhoAmIResponse)Service.Execute(Req);
                }
                
                InvalidLogin = false;
            }
            catch (SecurityNegotiationException ex)
            {
                InvalidLogin = true;
            }
            
        }

        public delegate void dInitServiceProxy(ref OrganizationServiceProxy Service, ServerConnection.Configuration Config, List<EntityContainer> EntityContainers);
        public void InitServiceProxy(ref OrganizationServiceProxy Service, ServerConnection.Configuration Config, List<EntityContainer> EntityContainers)
        {

            lock (CrmAsyncLock)
            {
                Service = ServerConnection.GetOrganizationProxy(Config);
                Service.EnableProxyTypes();
                RetrieveEntityList(Service, EntityContainers);
            }
            
        }

        public delegate void dCreateNewSolution(OrganizationServiceProxy Service, ref Guid SolutionId, String SelectedEntityName, Dictionary<String, byte[]> Files);
        public void CreateNewSolution(OrganizationServiceProxy Service, ref Guid SolutionId, String SelectedEntityName, Dictionary<String, byte[]> Files)
        {


            //Define a new publisher
            Publisher UsePublisher = null;
            Publisher BinderPublisher = new Publisher
            {
                UniqueName = "Crm2011EventBinder",
                FriendlyName = "Crm 2011 Event Binder",
                SupportingWebsiteUrl = "www.google.com",
                CustomizationPrefix = "eb",
                EMailAddress = "duke.mocanu@fmglobal.com",
                Description = "Creates a place holder publisher used for Crm2011EventBinder tool."
            };

            //Does publisher already exist?
            QueryExpression QueryPublisher = new QueryExpression
            {
                EntityName = Publisher.EntityLogicalName,
                ColumnSet = new ColumnSet("publisherid", "customizationprefix"),
                Criteria = new FilterExpression()
            };

            QueryPublisher.Criteria.AddCondition("uniquename", ConditionOperator.Equal, BinderPublisher.UniqueName);

            EntityCollection QueryPublisherResults = Service.RetrieveMultiple(QueryPublisher);

            Guid PublisherId = Guid.Empty;
            String Prefix;

            //If it already exists, use it
            if (QueryPublisherResults.Entities.Count > 0)
            {
                UsePublisher = (Publisher)QueryPublisherResults.Entities[0];
                PublisherId = (Guid)UsePublisher.PublisherId;
                Prefix = UsePublisher.CustomizationPrefix;
            }

            //If it doesn't exist, create it
            if (UsePublisher == null)
            {
                PublisherId = Service.Create(BinderPublisher);
                Prefix = BinderPublisher.CustomizationPrefix;
            }

            // Create a Solution
            //Define a solution
            Solution solution = new Solution
            {
                UniqueName = "EventBinder_" + Guid.NewGuid().ToString().Replace("-", "_"),
                FriendlyName = "Crm 2011 Event Binder - " + SelectedEntityName,
                PublisherId = new EntityReference(Publisher.EntityLogicalName, PublisherId),
                Description = "Temporary solution created by Crm2011EventBinder",
                Version = "1.0"
            };

            //Check whether it already exists
            QueryExpression queryCheckForCrmSolution = new QueryExpression
            {
                EntityName = Solution.EntityLogicalName,
                ColumnSet = new ColumnSet(),
                Criteria = new FilterExpression()
            };

            queryCheckForCrmSolution.Criteria.AddCondition("uniquename", ConditionOperator.Equal, solution.UniqueName);


            EntityCollection QueryCrmSolutionResults = Service.RetrieveMultiple(queryCheckForCrmSolution);
            Solution CrmSolutionResults = null;
            if (QueryCrmSolutionResults.Entities.Count > 0)
            {
                CrmSolutionResults = (Solution)QueryCrmSolutionResults.Entities[0];
                SolutionId = (Guid)CrmSolutionResults.SolutionId;
            }
            if (CrmSolutionResults == null)
            {
                SolutionId = Service.Create(solution);
            }

            //<snippetWorkWithSolutions5>
            // Add an existing Solution Component
            //Add the Account entity to the solution
            RetrieveEntityRequest ReqRetrieveEntity = new RetrieveEntityRequest()
            {
                LogicalName = SelectedEntityName
            };

            RetrieveEntityResponse RespRetrieveEntity = (RetrieveEntityResponse)Service.Execute(ReqRetrieveEntity);
            AddSolutionComponentRequest ReqAddComponent = new AddSolutionComponentRequest()
            {
                ComponentType = (int)componenttype.Entity,
                ComponentId = (Guid)RespRetrieveEntity.EntityMetadata.MetadataId,
                SolutionUniqueName = solution.UniqueName
            };

            ExportSolutionResponse exportSolutionResponse;

            lock (CrmAsyncLock)
            {

            Service.Execute(ReqAddComponent);

            ExportSolutionRequest exportSolutionRequest = new ExportSolutionRequest();
            exportSolutionRequest.Managed = false;
            exportSolutionRequest.ExportAutoNumberingSettings = false;
            exportSolutionRequest.ExportCalendarSettings = false;
            exportSolutionRequest.ExportCustomizationSettings = false;
            exportSolutionRequest.ExportEmailTrackingSettings = false;
            exportSolutionRequest.ExportGeneralSettings = false;
            exportSolutionRequest.ExportIsvConfig = false;
            exportSolutionRequest.ExportMarketingSettings = false;
            exportSolutionRequest.ExportOutlookSynchronizationSettings = false;
            exportSolutionRequest.ExportRelationshipRoles = false;
            exportSolutionRequest.SolutionName = solution.UniqueName;

            exportSolutionResponse = (ExportSolutionResponse)Service.Execute(exportSolutionRequest);

            }

            MemoryStream msOutput = new MemoryStream(exportSolutionResponse.ExportSolutionFile);

            ZipInputStream zip = new ZipInputStream(new MemoryStream(exportSolutionResponse.ExportSolutionFile, 0, exportSolutionResponse.ExportSolutionFile.Length));
            ZipFile file = new ZipFile(new MemoryStream(exportSolutionResponse.ExportSolutionFile, 0, exportSolutionResponse.ExportSolutionFile.Length));
            ZipEntry item;

            Files.Clear();

            while ((item = zip.GetNextEntry()) != null)
            {

                using (StreamReader sr = new StreamReader(file.GetInputStream(item)))
                {
                    byte[] b = UTF8Encoding.UTF8.GetBytes(sr.ReadToEnd());
                    Files.Add(item.Name, b);
                }
            }

            String AppDataSolutionFile = Path.Combine(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "CrmServer"), "solution.zip");

            if (File.Exists(AppDataSolutionFile)) 
                File.Delete(AppDataSolutionFile);

            FileStream fsOut = File.Create(AppDataSolutionFile);
            ZipOutputStream zipStream = new ZipOutputStream(fsOut);

            zipStream.SetLevel(3); //0-9, 9 being the highest level of compression
            zipStream.IsStreamOwner = true; // Makes the Close also Close the underlying stream
            zipStream.Close();
            zipStream.Dispose();
            zipStream = null;

        }

        public delegate void dSaveAndPublishSolution(OrganizationServiceProxy Service, String SelectedEntityName, Dictionary<String, byte[]> Files);
        public void SaveAndPublishSolution(OrganizationServiceProxy Service, String SelectedEntityName, Dictionary<String, byte[]> Files) 
        {

            String AppDataSolutionFile = Path.Combine(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "CrmServer"), "solution.zip");
            String AppDataSolutionC = Path.Combine(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "CrmServer"), "customizations.xml");
            String AppDataSolutionCC = Path.Combine(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "CrmServer"), "[Content_Types].xml");
            String AppDataSolutionS = Path.Combine(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "CrmServer"), "solution.xml");

            if (File.Exists(AppDataSolutionC)) File.Delete(AppDataSolutionC);
            if (File.Exists(AppDataSolutionCC)) File.Delete(AppDataSolutionCC);
            if (File.Exists(AppDataSolutionS)) File.Delete(AppDataSolutionS);

            File.WriteAllBytes(AppDataSolutionCC, Files["[Content_Types].xml"]);
            File.WriteAllBytes(AppDataSolutionC, Files["customizations.xml"]);
            File.WriteAllBytes(AppDataSolutionS, Files["solution.xml"]);

            ZipFile zipFile = new ZipFile(AppDataSolutionFile);

            zipFile.BeginUpdate();
            zipFile.Add(AppDataSolutionCC, "[Content_Types].xml");
            zipFile.Add(AppDataSolutionC, "customizations.xml");
            zipFile.Add(AppDataSolutionS, "solution.xml");
            zipFile.CommitUpdate();
            zipFile.Close();
            zipFile = null;

            GC.Collect();

            ImportSolutionRequest ReqImportSolution = new ImportSolutionRequest()
            {
                CustomizationFile = File.ReadAllBytes(AppDataSolutionFile)
            };

            lock (CrmAsyncLock)
            {
                ImportSolutionResponse RespImportSolution = (ImportSolutionResponse)Service.Execute(ReqImportSolution);
                PublishXmlRequest ReqPublish = new PublishXmlRequest();
                ReqPublish.ParameterXml = String.Format(@"<importexportxml><entities><entity>{0}</entity></entities><nodes/><securityroles/><settings/><workflows/></importexportxml>", SelectedEntityName);
                PublishXmlResponse RespPublish = (PublishXmlResponse)Service.Execute(ReqPublish);
            }
            
        }

        public delegate void dDeleteSolution(OrganizationServiceProxy Service, Guid SolutionId);
        public void DeleteSolution(OrganizationServiceProxy Service, Guid SolutionId)
        {
            lock (CrmAsyncLock)
            {
                Service.Delete("solution", SolutionId);
            }
        }

        public delegate void dGetWebResources(OrganizationServiceProxy Service, List<String> Webresources);
        public void GetWebResources(OrganizationServiceProxy Service, List<String> Webresources)
        {

            ConditionExpression ce = new ConditionExpression();
            ce.AttributeName = "webresourcetype";
            ce.Operator = ConditionOperator.Equal;
            ce.Values.Add(3);

            FilterExpression fe = new FilterExpression();
            fe.FilterOperator = LogicalOperator.And;
            fe.AddCondition(ce);

            ColumnSet cs = new ColumnSet();
            cs.AddColumn("name");

            QueryExpression qe = new QueryExpression();
            qe.EntityName = "webresource";
            qe.ColumnSet = cs;
            qe.Criteria = fe;

            EntityCollection ecol;

            lock (CrmAsyncLock)
            {
                ecol = Service.RetrieveMultiple(qe);
            }
            
            Webresources.Clear();
            
            foreach (Entity e in ecol.Entities)
                Webresources.Add((string) e.Attributes["name"]);

            Webresources.Sort();

        }
    }
}
